/*
 *  Clogger - Lightweight C Logging Middleware
 *
 *  A thread-safe, asynchronous logging library for C projects, supporting
 *  log levels, file rotation, multi-process shared memory pools, and queue-based
 *  asynchronous writing.
 *
 *  Copyright (C) 2024 JamesBin
 *
 *  Key features:
 *  - Multi-level logging (CLOG_DEBUG, INFO, WARNING, ERROR, FATAL)
 *  - Asynchronous log writing via thread-safe queues
 *  - Shared memory pool support for inter-process resource reuse
 *  - Configurable log output (console, file, or both)
 *  - Automatic log file rotation (size-based)
 *
 *  This file is part of Clogger.
 *
 *  Clogger is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Clogger is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Clogger.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/time.h>
#include <pthread.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>

#include "log_entry_write.h"
#include "clog_json_utils.h"
#include "ring_buf.h"
#include "clog_log.h"
#include "clog_common.h"
#include "clog_lib.h"
#include "share_mem.h"
#include "cJSON.h"


static clog_config_t *g_clog_cfg = NULL;
static thread_pool_t g_dump_thread_pool;

/**
 * @brief Create a directory if it does not exist.
 *
 * This function creates a directory at the specified path if it does not already exist.
 * It checks the path for each '/' character and creates intermediate directories as needed.
 * Error handling is included for invalid parameters and mkdir failures.
 *
 * @param path_name Path of the directory to create.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Invalid parameters
 *         - -RET_ERR: mkdir failure
 */
static int32_t create_dir(const char *path_name)
{
	char dir_name[256] = {0};
	uint32_t i, len;

	strncpy(dir_name, path_name, sizeof(dir_name) - 1);//NOLINT
    dir_name[sizeof(dir_name) - 1] = '\0';
	len = (int32_t)strlen(dir_name);

	for (i = 1; i < len; i++) {
		if (dir_name[i] == '/') {
			dir_name[i] = 0;
			if (access(dir_name, 0) != 0) {
				if (mkdir(dir_name, 0755) == -1) {
					clog_log_error("mkdir %s error\n", path_name);
					return -1;
				}
			}
			dir_name[i] = '/';
		}
	}

    if (access(dir_name, 0) != 0) {
		if (mkdir(dir_name, 0755) == -1) {
			clog_log_error("mkdir %s error\n", dir_name);
			return -1;
		}
	}

	return 0;
}

/**
 * @brief Calculate absolute timeout time from current time + milliseconds.
 *
 * This function calculates absolute timeout time using CLOCK_MONOTONIC (unaffected by system time changes).
 * Converts milliseconds to timespec struct (seconds + nanoseconds).
 *
 * @param ms Timeout in milliseconds.
 * @param timeout Output parameter to store calculated absolute timeout.
 */
static void calculate_absolute_timeout(uint32_t ms, struct timespec *timeout) {
    clock_gettime(CLOCK_REALTIME, timeout);
    timeout->tv_sec += ms / 1000;
    timeout->tv_nsec += (ms % 1000) * 1000000;

    if (timeout->tv_nsec >= 1000000000) {
        timeout->tv_sec++;
        timeout->tv_nsec -= 1000000000;
    }
}

/**
 * @brief Get current timestamp in milliseconds.
 *
 * This function returns the current time in milliseconds using CLOCK_REALTIME.
 * It combines seconds and nanoseconds from the system clock.
 *
 * @return uint64_t Current timestamp in milliseconds.
 */
static uint64_t get_current_timestamp(void)
{
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}

/**
 * @brief Enqueue a task to the dump thread pool task queue.
 *
 * This function enqueues a task (module_info_t) to the dump thread pool's task queue.
 * It locks the queue mutex, checks if the queue is full, and enqueues the task if space is available.
 * If the queue is full, it unlocks the mutex and logs a warning.
 *
 * @param module Pointer to the module_info_t structure representing the module to be enqueued.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Task queue overflow, module dump delayed
 */
static int32_t enqueue_task(module_info_t *module, uint8_t task_type, uint8_t priority)
{
    int32_t ret = RET_OK;
    int32_t task_index;
    int32_t new_task_index;
    task_queue_t *queue = &g_dump_thread_pool.task_queue;

    /* check if the module is already in the queue */
    if (module->is_task_enqueued) {
        return RET_OK;
    }
    module->is_task_enqueued = 1;

    pthread_mutex_lock(&queue->mutex);
    if ((queue->rear + 1) % TASK_QUEUE_CAPACITY == queue->front) {
        pthread_mutex_unlock(&queue->mutex);
        clog_log_warn("task queue overflow, module %s dump delayed", module->module_name);
        return -RET_ERR;
    }

    queue->tasks[queue->rear] = (dump_task_t){module};
    queue->rear = (queue->rear + 1) % TASK_QUEUE_CAPACITY;
    new_task_index = (queue->rear - 1 + TASK_QUEUE_CAPACITY) % TASK_QUEUE_CAPACITY;
    queue->tasks[new_task_index].task_type = task_type;
    queue->tasks[new_task_index].priority = priority;
    queue->tasks[new_task_index].enqueue_timestamp = get_current_timestamp();
    
    pthread_cond_signal(&queue->cond);
    pthread_mutex_unlock(&queue->mutex);
    return RET_OK;
}

/**
 * @brief Dequeue a task from the dump thread pool task queue.
 *
 * This function dequeues a task (module_info_t) from the dump thread pool's task queue.
 * It locks the queue mutex, waits if the queue is empty, and dequeues the task if available.
 * If the thread pool is not running, it returns an empty task.
 *
 * @return dump_task_t The dequeued task (module_info_t) or an empty task if the thread pool is not running.
 */
static dump_task_t dequeue_task(void)
{
    pthread_mutex_lock(&g_dump_thread_pool.task_queue.mutex);

    while ((g_dump_thread_pool.task_queue.front == g_dump_thread_pool.task_queue.rear) &&
            g_dump_thread_pool.running) {
        pthread_cond_wait(&g_dump_thread_pool.task_queue.cond, &g_dump_thread_pool.task_queue.mutex);
    }

    if (!g_dump_thread_pool.running) {
        pthread_mutex_unlock(&g_dump_thread_pool.task_queue.mutex);
        return (dump_task_t){NULL};
    }

    dump_task_t task = g_dump_thread_pool.task_queue.tasks[g_dump_thread_pool.task_queue.front];
    g_dump_thread_pool.task_queue.front = (g_dump_thread_pool.task_queue.front + 1) % TASK_QUEUE_CAPACITY;
    pthread_mutex_unlock(&g_dump_thread_pool.task_queue.mutex);
    return task;
}

/**
 * @brief Rotate and organize log files for a module.
 *
 * This function rotates and organizes log files for a specific module.
 * It closes the current log file, renames existing log files, and opens a new log file.
 * Error handling is included for invalid module info, file operations, and log file rotation.
 *
 * @param module_info Pointer to the module_info_t structure containing module configuration.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Invalid module info
 *         - -RET_ERR: Log file is null
 *         - -RET_ERR: File closing failed
 *         - -RET_ERR: Log file rotation failed
 *         - -RET_ERR: File opening failed
 */
static int32_t rotate_and_organize_log_files(module_info_t *module_info)
{
    int32_t ret = RET_OK;
    int32_t s_index = 0;
    uint32_t version = 0;
    char new_file_name[LOG_FILE_PATH_LEN];
    char old_file_name[LOG_FILE_PATH_LEN];

    if(module_info->log_file_fp == NULL) {
        return -RET_ERR;
    }

    ret = fclose(module_info->log_file_fp);
    if (ret != 0) {
        clog_log_error("dump buf data to file: close file failed \n");
        return -RET_ERR;
    }
    module_info->log_file_fp = NULL;

    while (1) {
        if(version >= 10) {
            break;
        }
        snprintf(new_file_name, sizeof(new_file_name), "%.244s.%u",
                module_info->log_file_name, version);
        if (access(new_file_name, F_OK) == -1) {
            break;
        }
        version++;
    }

    for(s_index = version - 1; s_index > 0; s_index--) {
        /* get the old file name */
        snprintf(old_file_name, sizeof(old_file_name), "%.244s.%u",
                module_info->log_file_name, s_index);
        /* set the new file name */
        snprintf(new_file_name, sizeof(new_file_name), "%.244s.%u",
                module_info->log_file_name, s_index + 1);
        if (access(old_file_name, F_OK) == 0) {
            if (rename(old_file_name, new_file_name) == -1) {
                clog_log_error("error renaming file: %s\n", old_file_name);
                return -RET_ERR;
            }
            clog_log_info("rename %s to %s success \n", old_file_name, new_file_name);
        }
    }

    if (rename(module_info->log_file_name, new_file_name) == -1) {
        clog_log_error("Error renaming file: %s\n", module_info->log_file_name);
        return -RET_ERR;
    }
            
    module_info->log_file_fp = fopen(module_info->log_file_name, "w+");
    if (module_info->log_file_fp == NULL) {
        clog_log_error("dump buf data to file: open file failed, file name: %s, error: %s \n",
                module_info->log_file_name, strerror(errno));

        module_info->log_file_fp = fopen(module_info->log_file_name, "a+");
        if (module_info->log_file_fp == NULL) {
            clog_log_error("retry open file failed, error: %s \n", strerror(errno));
            return -RET_ERR;
        }
    }

    clog_log_debug("rotate message and rename log file: %s success \n", new_file_name);
    return RET_OK;
}

/**
 * @brief Write a log message to the ring buffer.
 *
 * This function writes a log message to the ring buffer. It checks the validity of the data
 * and size parameters, and then puts the data into the ring buffer. Error handling is included
 * for invalid parameters, empty data, and buffer overflow.
 *
 * @param data Pointer to the log message data.
 * @param size Size of the log message data.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Invalid parameters
 *         - -RET_ERR: Empty data
 *         - -RET_ERR: Buffer overflow
 */
static int32_t write_one_log_message_to_ring_buf(uint8_t *data, size_t size)
{
    int32_t ret = RET_OK;

    if (data == NULL) {
        clog_log_error("write data to log buf fail, data is null \n");
        return -RET_ERR;
    }

    if (size == 0) {
        clog_log_error("write data to log buf fail, size is 0 \n");
        return -RET_ERR;
    }

    ret = put_data_to_ring_buf_interface(data, size);
    if (ret < 0) {
        clog_log_error("put_data_to_ring_buf_interface fail, ret = %d \n", ret);
        return -ret;
    }

    return ret;
}

/**
 * @brief Read a log message from the ring buffer.
 *
 * This function reads a log message from the ring buffer and stores it in the provided data buffer.
 * It checks the validity of the data buffer and the size pointer, and then retrieves the log message
 * from the ring buffer. Error handling is included for invalid parameters, empty buffer, and buffer overflow.
 *
 * @param data Pointer to the buffer where the log message will be stored.
 * @param size Pointer to the variable that will store the size of the log message.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Invalid parameters
 *         - -RET_ERR: Empty buffer
 *         - -RET_ERR: Buffer overflow
 */
static int32_t read_one_log_message_from_ring_buf(uint8_t *data, size_t *size)
{
    int32_t ret = RET_OK;

    if (data == NULL) {
        clog_log_error("read data from log buf fail, data is null \n");
        return -RET_ERR;
    }

    ret = get_data_from_ring_buf_interface(data, size);
    if (ret < 0) {
        clog_log_error("read data from log buf fail, ret = %d \n", ret);
        return -ret;
    }

    return ret;
}

/**
 * @brief Timer check thread function.
 *
 * This function is the timer check thread for the clog library. It continuously checks the time
 * difference between the current time and the last write time for each module. If the time difference
 * exceeds the async interval, a timer trigger task is enqueued for that module. Error handling is
 * included for NULL configuration, module enable check, and task enqueue.
 *
 * @param arg Pointer to the thread argument (not used).
 * @return void* NULL (always).
 */
static void* dump_timer_check_thread(void* arg)
{
    struct timespec timeout;
    uint64_t current_time;
    uint32_t s_index = 0;
    uint32_t async_interval = g_clog_cfg->global_info.async_interval;
    module_info_t *module_info = NULL;

    if (!g_clog_cfg) {
        clog_log_error("timer check thread: g_clog_cfg is NULL");
        return NULL;
    }

    while (g_dump_thread_pool.running) {
        current_time = get_current_timestamp();
        for (s_index = 0; s_index < g_clog_cfg->module_info_list.module_num; s_index++) {
            module_info = &g_clog_cfg->module_info_list.module_info[s_index];

            if (module_info->module_enable == 0) continue;

            pthread_mutex_lock(&module_info->tmp_buf_mutex);
            if (module_info->tmp_buf_size > 0 && 
                (current_time - module_info->last_write_time) > async_interval) {
                enqueue_task(module_info, TASK_TYPE_DUMP, TASK_PRIORITY_LOW);
            }
            pthread_mutex_unlock(&module_info->tmp_buf_mutex);
        }

        usleep(async_interval * 1000);
    }
    return NULL;
}

/**
 * @brief Thread pool worker function.
 *
 * This function is the worker thread for the thread pool. It continuously dequeues tasks from the
 * task queue, processes them, and writes the log messages to the corresponding log files. Error
 * handling is included for empty tasks, module ID check, and log file write errors.
 *
 * @param arg Pointer to the thread pool structure.
 * @return void* NULL (always).
 */
static void* dump_thread_pool_worker(void* arg)
{
    dump_task_t task;
    module_info_t *module;
    int32_t ret;
    static uint32_t log_file_size = 0;

    while (g_dump_thread_pool.running) {
        task = dequeue_task();
        module = task.module;
        if (!module) continue; 

        pthread_mutex_lock(&module->tmp_buf_mutex);
        module->is_task_enqueued = false;
        if (module->tmp_buf_size == 0) {
            pthread_mutex_unlock(&module->tmp_buf_mutex);
            continue;
        }

        ret = fwrite(module->tmp_buf, 1, module->tmp_buf_size, module->log_file_fp);
        if (ret < 0) {
            clog_log_error("dump buf data to file: write data to file failed \n");
            pthread_mutex_unlock(&module->tmp_buf_mutex);
            module->tmp_buf_size = 0;
            break;
        }

        ret = fflush(module->log_file_fp);
        if (ret != 0) {
            clog_log_error("dump buf data to file: flush file failed \n");
            pthread_mutex_unlock(&module->tmp_buf_mutex);
            module->tmp_buf_size = 0;
            break;
        }
        module->last_write_time = get_current_timestamp();
        module->log_file_size += module->tmp_buf_size;
        module->tmp_buf_size = 0;

        if(module->log_file_size > g_clog_cfg->global_info.log_file_size) {
            clog_log_info("log file size: %d, rotate message to file\n",
                    module->log_file_size);
            ret = rotate_and_organize_log_files(module);
            if (ret < 0) {
                clog_log_error("rotate_and_organize_log_files fail, ret: %d \n", ret);
                pthread_mutex_unlock(&module->tmp_buf_mutex);
                break;
            }
            module->log_file_size = 0;
        }
        pthread_mutex_unlock(&module->tmp_buf_mutex);
    }
    return NULL;
}

/**
 * @brief Save log message to module temporary buffer.
 *
 * This function saves a log message to the temporary buffer of a specific module.
 * It checks the module ID, component ID, and data size, and then copies the data
 * to the module's temporary buffer. Error handling is included for invalid parameters,
 * module ID, component ID, and buffer overflow.
 *
 * @param data Pointer to the log message data.
 * @param size Size of the log message data.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Invalid parameters
 *         - -RET_ERR: Module ID or component ID check failed
 *         - -RET_ERR: Buffer overflow
 */
static int32_t save_log_message_to_module_buf(uint8_t *data, size_t size)
{
    int32_t ret = RET_OK;
    int8_t module_id = 0;
    int8_t comp_id = 0;
    size_t data_len = 0;
    module_info_t *module_info = NULL;
    clog_entry_t *entry = (clog_entry_t *)data;
    if (data == NULL || size <= 2) {
        clog_log_error("log message save to module buf: invalid param\n");
        return -RET_ERR;
    }

    module_id = data[0];
    comp_id = data[1];

    if(module_id != ~comp_id) {
        clog_log_error("check module_id: %d, comp_id: %d error, module_id != ~comp_id \n",
                module_id, comp_id);
        return -RET_ERR;
    }

    /* get module info by module_id */
    if (module_id >= (int8_t)(g_clog_cfg->module_info_list.module_num & 0XFF)) {
        clog_log_error("check module_id: %d error, module_id >= module_num \n", module_id);
        return -RET_ERR;
    }
    module_info = &g_clog_cfg->module_info_list.module_info[module_id];

    /* check module enable */
    if (module_info->module_enable == 0) {
        clog_log_error("module %s is disabled, skip this message\n", module_info->module_name);
        return -RET_ERR;
    }

    data_len = size - LOG_HEADER_SIZE;
    /* check module id */
    if ((int8_t)(module_info->module_id & 0XFF) == module_id) {
        if(module_info->tmp_buf_size + data_len > TMP_MODULE_BUF_SIZE) {
            clog_log_error("module %s tmp buf overflow\n", module_info->module_name);
            return -RET_ERR;
        }

        /* copy data to module tmp buf */
        pthread_mutex_lock(&module_info->tmp_buf_mutex);
        memcpy(module_info->tmp_buf + module_info->tmp_buf_size, 
                data + LOG_HEADER_SIZE, 
                data_len);
        module_info->tmp_buf_size += data_len;

        if (module_info->tmp_buf_size >= g_clog_cfg->global_info.async_buf_size) {
            enqueue_task(module_info, TASK_TYPE_DUMP, TASK_PRIORITY_HIGH);
        }

        pthread_mutex_unlock(&module_info->tmp_buf_mutex);
    } else {
        clog_log_error("not find module id: %d \n", module_id);
        return -RET_ERR;
    }

    return ret;
}

/**
 * @brief Read ring buffer data to module temporary buffers.
 *
 * This function reads data from the global ring buffer and distributes it to module-specific temporary buffers.
 * It continuously checks the ring buffer for available data, reads it, and processes it.
 * Error handling is included for invalid configuration, data reading, and processing.
 *
 * @param arg Pointer to the clog_config_t structure containing module configuration.
 * @return void* NULL on success; NULL on failure
 */
void* read_ring_buf_data_to_module_buf_thread(void* arg)
{
    int32_t ret = RET_OK;
    uint8_t *tmp_buf = NULL;
    uint32_t used_size = 0;
    size_t data_len = 0;

    (void)arg; /* unused */

    tmp_buf = malloc(TMP_MODULE_BUF_SIZE);
    if (tmp_buf == NULL) {
        clog_log_error("malloc tmp buf fail \n");
        return NULL;
    }

    while (!g_clog_cfg->global_info.should_stop) {
        /* if there are no new messages, interface will block */
        ret = read_one_log_message_from_ring_buf(tmp_buf, &data_len);
        if (ret < 0) {
            clog_log_error("read_one_log_message_from_ring_buf fail, ret: %d \n", ret);
            continue;
        }

        if (data_len > TMP_MODULE_BUF_SIZE) {
            clog_log_error("log data too large, len: %zu, buf_size: %d \n", 
                   data_len, TMP_MODULE_BUF_SIZE);
            continue;
        }

        ret = save_log_message_to_module_buf(tmp_buf, data_len);
        if(ret < 0) {
            clog_log_error("save_log_message_to_module_buf fail, ret: %d \n", ret);
            continue;
        }
    }

    free(tmp_buf);

    return NULL;
}

/**
 * @brief Initialize log files for modules.
 *
 * This function initializes log files for each module specified in the configuration.
 * It creates log file directories, opens log files, and sets up necessary data structures.
 * Error handling is included for invalid configuration, directory creation, and file opening.
 *
 * @param cfg Pointer to the clog_config_t structure containing module configuration.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Global log directory is null
 *         - -RET_ERR: Module log file directory creation failed
 *         - -RET_ERR: Log file creation failed
 */
static int32_t init_module_log_files(clog_config_t *cfg)
{
    int32_t ret = RET_OK;
    uint32_t i = 0;
    module_info_t *module_info = NULL;
    char log_file_path[LOG_FILE_PATH_LEN] = {0};

    if (cfg == NULL) {
        clog_log_error("init module log files fail, cfg is null \n");
        return -RET_ERR;
    }

    for (i = 0; i < cfg->module_info_list.module_num; i++) {
        module_info = &cfg->module_info_list.module_info[i];
        if (module_info->module_enable == 0) {
            clog_log_info("module %s is disabled, skip init log file\n", module_info->module_name);
            continue;
        }

        /* create module log file directory */
        snprintf(log_file_path, sizeof(log_file_path), "%s/%s",
                cfg->global_info.log_dir, module_info->module_name);
        ret = create_dir(log_file_path);
        if (ret < 0) {
            clog_log_error("create module log file directory fail, path: %s \n", log_file_path);
            return ret;
        }

        /* create log file message and save to log_file_fp */
        snprintf(module_info->log_file_name, sizeof(module_info->log_file_name),
                "%s/%s", log_file_path, LOG_FILE_NAME);
        module_info->log_file_fp = fopen(module_info->log_file_name, "w+");  
        if (module_info->log_file_fp == NULL) {
            clog_log_error("create log file fail, fopen %s fail \n", module_info->log_file_name);
            return -ERR_CODE_OPEN_FILE;
        }

        clog_log_info("create log file success, path: %s\n", log_file_path);
    }

    return ret;
}

/**
 * @brief Deinitialize module log files.
 *
 * This function deinitializes log files for each module specified in the configuration.
 * It closes log file handles, frees associated memory, and performs cleanup operations.
 * Error handling is included for invalid configuration, file closing, and memory deallocation.
 *
 * @param cfg Pointer to the clog_config_t structure containing module configuration.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Global configuration is null
 *         - -RET_ERR: Log file closing failed
 *         - -RET_ERR: Memory deallocation failed
 */
static int32_t deinit_module_log_files(clog_config_t *cfg)
{
    int32_t ret = RET_OK;
    uint32_t i = 0;

    module_info_t *module_info = NULL;

    if (cfg == NULL) {
        clog_log_error("deinit module log files fail, cfg is null \n");
        return -RET_ERR;
    }

    for (i = 0; i < cfg->module_info_list.module_num; i++) {
        module_info = &cfg->module_info_list.module_info[i];
        if (module_info->module_enable == 0) {
            clog_log_error("module %s is disabled, skip deinit log file\n", module_info->module_name);
            continue;
        }

        ret = fclose(module_info->log_file_fp);
        if (ret != 0) {
            clog_log_error("close log file fail, fclose %s fail \n", module_info->module_name);
            return -RET_ERR;
        }
    }

    return ret;
}

/**
 * @brief Initialize module temporary buffers and mutexes.
 *
 * This function initializes temporary buffers and mutexes for each module specified in the configuration.
 * It allocates memory for each module's temporary buffer and initializes a mutex for thread synchronization.
 * Error handling is included for invalid configuration, memory allocation, and mutex initialization.
 *
 * @param cfg Pointer to the clog_config_t structure containing module configuration.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Global configuration is null
 *         - -RET_ERR: Memory allocation for temporary buffer failed
 *         - -RET_ERR: Mutex initialization failed
 */
static int32_t init_module_buf_and_mutex(clog_config_t *cfg)
{
    int32_t ret = RET_OK;
    uint32_t i = 0;
    module_info_t *module_info = NULL;

    if (cfg == NULL) {
        clog_log_error("init module tmp buf fail, cfg is null \n");
        return -RET_ERR;
    }

    clog_log_info("init module tmp buf and mutex\n");

    for (i = 0; i < cfg->module_info_list.module_num; i++) {
        module_info = &cfg->module_info_list.module_info[i];
        if (module_info->module_enable == 0) {
            clog_log_error("module %s is disabled, skip init tmp buf\n", module_info->module_name);
            continue;
        }

        module_info->tmp_buf = malloc(TMP_MODULE_BUF_SIZE);
        if (module_info->tmp_buf == NULL) {
            clog_log_error("malloc module %s tmp buf fail \n", module_info->module_name);
            return -RET_ERR;
        }

        ret = pthread_mutex_init(&module_info->tmp_buf_mutex, NULL);
        if (ret != 0) {
            clog_log_error("init module %s tmp buf mutex fail \n", module_info->module_name);
            return -RET_ERR;
        }

        module_info->tmp_buf_size = 0;

        clog_log_info("init module %s tmp buf success\n", module_info->module_name);
    }

    return ret;
}

/**
 * @brief Deinitialize module temporary buffers and mutexes.
 *
 * This function deinitializes temporary buffers and mutexes for each module specified in the configuration.
 * It frees the memory allocated for each module's temporary buffer and destroys the associated mutex.
 * Error handling is included for invalid configuration, memory deallocation, and mutex destruction.
 *
 * @param cfg Pointer to the clog_config_t structure containing module configuration.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Global configuration is null
 *         - -RET_ERR: Memory deallocation for temporary buffer failed
 *         - -RET_ERR: Mutex destruction failed
 */
static int32_t deinit_module_buf_and_mutex(clog_config_t *cfg)
{
    int32_t ret = RET_OK;
    uint32_t i = 0;
    module_info_t *module_info = NULL;

    if (cfg == NULL) {
        clog_log_error("deinit module tmp buf fail, cfg is null \n");
        return -RET_ERR;
    }

    for (i = 0; i < cfg->module_info_list.module_num; i++) {
        module_info = &cfg->module_info_list.module_info[i];
        if (module_info->module_enable == 0) {
            clog_log_error("module %s is disabled, skip deinit dump thread\n", module_info->module_name);
            continue;
        }

        ret = pthread_mutex_destroy(&module_info->tmp_buf_mutex);
        if (ret != 0) {
            clog_log_error("destroy module %s tmp buf mutex fail \n", module_info->module_name);
            return -RET_ERR;
        }

        module_info->is_task_enqueued = false;

        free(module_info->tmp_buf);
        module_info->tmp_buf = NULL;
        module_info->tmp_buf_size = 0;

        clog_log_info("deinit module %s tmp buf success\n", module_info->module_name);
    }

    return ret;
}

/**
 * @brief Initialize a task queue.
 *
 * This function initializes a task queue structure, setting up the front and rear indices,
 * initializing the mutex and condition variable for thread synchronization, and setting the
 * running flag to true.
 *
 * @param queue Pointer to the task_queue_t structure to initialize.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Mutex initialization failed
 *         - -RET_ERR: Condition variable initialization failed
 */
static int32_t task_queue_init(task_queue_t *queue)
{
    memset(queue, 0, sizeof(task_queue_t));
    if (pthread_mutex_init(&queue->mutex, NULL) != 0) {
        clog_log_error("task queue mutex init failed");
        return -RET_ERR;
    }
    if (pthread_cond_init(&queue->cond, NULL) != 0) {
        clog_log_error("task queue cond init failed");
        pthread_mutex_destroy(&queue->mutex);
        return -RET_ERR;
    }
    return RET_OK;
}

/**
 * @brief Initialize read ring buffer data thread.
 *
 * This function initializes a thread that reads data from the global ring buffer and distributes it to module-specific temporary buffers.
 * Error handling is included for invalid configuration and thread creation.
 *
 * @param cfg Pointer to the clog_config_t structure containing module configuration.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Global configuration is null
 *         - -RET_ERR: Thread creation failed
 */
static int32_t init_read_ring_buf_data_thread(clog_config_t *cfg)
{
    int32_t ret = RET_OK;
    uint32_t i = 0;
    module_info_t *module_info = NULL;

    if (cfg == NULL) {
        clog_log_error("init dump module buf to file thread fail, cfg is null \n");
        return -RET_ERR;
    }
    clog_log_info("init read ring buf data thread\n");
    cfg->global_info.should_stop = false;
    ret = pthread_create(&cfg->global_info.read_thread, NULL, read_ring_buf_data_to_module_buf_thread, NULL);
    if (ret != 0) {
        clog_log_error("create read ring buf data thread failed\n");
        return -RET_ERR;
    }

    g_dump_thread_pool.running = true;
    task_queue_init(&g_dump_thread_pool.task_queue);
    for (int i = 0; i < MAX_WORKER_THREADS; i++) {
        ret = pthread_create(&g_dump_thread_pool.workers[i], NULL, dump_thread_pool_worker, NULL);
        if (ret != 0) {
            clog_log_error("create worker thread %d failed", i);
            g_dump_thread_pool.running = false;
            return -RET_ERR;
        }
    }

    ret = pthread_create(&g_dump_thread_pool.dump_check_pthread, NULL, dump_timer_check_thread, NULL);
    if (ret != 0) {
        clog_log_error("create dump timer check thread failed");
        return -RET_ERR;
    }

    return ret;
}

/**
 * @brief Deinitialize read ring buffer data thread.
 *
 * This function deinitializes the thread that reads data from the global ring buffer and distributes it to module-specific temporary buffers.
 * Error handling is included for invalid configuration and thread joining.
 *
 * @param cfg Pointer to the clog_config_t structure containing module configuration.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Global configuration is null
 *         - -RET_ERR: Thread joining failed
 */
static int32_t deinit_read_ring_buf_data_thread(clog_config_t *cfg)
{
    int32_t ret = RET_OK;
    uint32_t i = 0;
    module_info_t *module_info = NULL;

    if (cfg == NULL) {
        clog_log_error("deinit dump module buf to file thread fail, cfg is null \n");
        return -RET_ERR;
    }

    cfg->global_info.should_stop = true;
    ret = pthread_join(cfg->global_info.read_thread, NULL);
    if (ret != 0) {
        clog_log_error("join read ring buf data thread failed\n");
        return -RET_ERR;
    }

    /* wait for worker threads to finish */
    g_dump_thread_pool.running = false;
    pthread_mutex_lock(&g_dump_thread_pool.task_queue.mutex);
    pthread_cond_broadcast(&g_dump_thread_pool.task_queue.cond);
    pthread_mutex_unlock(&g_dump_thread_pool.task_queue.mutex);
    for (int i = 0; i < MAX_WORKER_THREADS; i++) {
        pthread_join(g_dump_thread_pool.workers[i], NULL);
    }

    pthread_join(g_dump_thread_pool.dump_check_pthread, NULL);

    return ret;
}

/**
 * @brief Initialize log ring buffer.
 *
 * This function initializes the log ring buffer with the specified size.
 * It checks for valid configuration, allocates memory for the buffer,
 * and initializes the ring buffer interface.
 *
 * @param cfg Pointer to the clog_config_t structure containing configuration.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Configuration is null
 *         - -RET_ERR: Buffer size is zero
 *         - -RET_ERR: Ring buffer initialization fails
 */
static int32_t init_log_ring_buffer(clog_config_t *cfg)
{
    int32_t ret = RET_OK;
    size_t buf_size = 0;

    if (cfg == NULL) {
        clog_log_error("init log buffer fail, cfg is null \n");
        return -RET_ERR;
    }
    clog_log_info("init log buffer, buf_size: %zu \n", cfg->global_info.ring_bufs_size);
    buf_size = cfg->global_info.ring_bufs_size;
    if (buf_size == 0) {
        clog_log_error("init log buffer fail, buf_size is 0 \n");
        return -RET_ERR;
    }

    ret = ring_buf_init_interface(buf_size);
    if (ret < 0) {
        clog_log_error("init log ring buffer fail, ret: %d \n", ret);
        return -RET_ERR;
    }

    ret = set_ring_buf_debug_level_interface(cfg->global_info.debug_level);
    if (ret < 0) {
        clog_log_error("set ring buf debug level fail \n");
        return -RET_ERR;
    }

    return ret;
}

/**
 * @brief Deinitialize log ring buffer.
 *
 * This function deinitializes the log ring buffer, freeing allocated memory
 * and resetting the ring buffer interface.
 *
 * @param cfg Pointer to the clog_config_t structure containing configuration.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Configuration is null
 *         - -RET_ERR: Ring buffer deinitialization fails
 */
static int32_t deinit_log_ring_buffer(clog_config_t *cfg)
{
    int32_t ret = RET_OK;

    if (cfg == NULL) {
        clog_log_error("deinit log ring buffer fail, cfg is null\n");
        return -RET_ERR;
    }

    ret = ring_buf_deinit_interface();
    if (ret < 0) {
        clog_log_error("deinit log ring buffer fail, ret: %d \n", ret);
        return -ret;
    }

    return ret;
}

/**
 * @brief Initialize clog system interface.
 *
 * This function initializes the clog system interface by loading configuration from a JSON file,
 * initializing log files, log ring buffer, module buffers, and mutexes.
 *
 * @param cfg_file Path to the JSON configuration file.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Configuration file path is null or empty
 *         - -RET_ERR: JSON file parse error
 *         - -RET_ERR: Log file initialization fails
 *         - -RET_ERR: Log ring buffer initialization fails
 *         - -RET_ERR: Module buffer and mutex initialization fails
 *         - -RET_ERR: Thread creation fails
 */
static int32_t clog_system_init(const char *cfg_file)
{
    int32_t ret = RET_OK;

    if ((cfg_file == NULL) || (strlen(cfg_file) == 0u)) {
        clog_log_error("clog system init fail, cfg_file: %s \n", cfg_file);
        return -RET_ERR;
    }

    clog_log_info("clog system init start, cfg_file: %s\n", cfg_file);

    g_clog_cfg = (clog_config_t *)malloc(sizeof(clog_config_t));
    if (g_clog_cfg == NULL) {
        clog_log_error("clog system init fail, malloc g_clog_cfg \n");
        return -ERR_CODE_MALLOC;
    }

    /* json file parse: file to cJSON */
    ret = clog_json_parse_file(cfg_file, g_clog_cfg);
    if (ret < 0) {
        clog_log_error("json file parse error\n");
        goto err;
    }

    ret = init_module_log_files(g_clog_cfg);
    if (ret < 0) {
        clog_log_error("init module log files fail \n");
        goto err;
    }

    ret = shmem_ring_init(SHM_NAME, g_clog_cfg->global_info.shm_size);
    if (ret < 0) {
        clog_log_error("shmem_ring_init failed, ret: %d\n", ret);
        goto err;
    }

    ret = init_log_ring_buffer(g_clog_cfg);
    if (ret < 0) {
        clog_log_error("init_log_ring_buffer failed, ret: %d\n", ret);
        goto err;
    }

    ret = init_module_buf_and_mutex(g_clog_cfg);
    if (ret < 0) {
        goto err;
    }

    ret = init_read_ring_buf_data_thread(g_clog_cfg);
    if (ret < 0) {
        clog_log_error("init read ring buf data thread fail \n");
        goto err;
    }

    return ret;

err:
    free(g_clog_cfg);
    g_clog_cfg = NULL;
    return ret;
}

/**
 * @brief Deinitialize clog system interface.
 *
 * This function deinitializes the clog system interface by deinitializing log ring buffer,
 * module buffers, and mutexes, and freeing allocated memory.
 *
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Log ring buffer deinitialization fails
 *         - -RET_ERR: Module buffer and mutex deinitialization fails
 *         - -RET_ERR: Thread deinitialization fails
 */
static int32_t clog_system_deinit(void)
{
    int32_t ret = RET_OK;

    ret = clog_json_free();
    if (ret < 0) {
        clog_log_error("deinit json root fail \n");
        return -ret;
    }

    ret = deinit_log_ring_buffer(g_clog_cfg);
    if (ret < 0) {
        clog_log_error("deinit log ring buffer fail \n");
        return -ret;
    }

    ret = deinit_module_buf_and_mutex(g_clog_cfg);
    if (ret < 0) {
        clog_log_error("deinit module tmp buf fail \n");
        return -ret;
    }

    ret = shmem_ring_destroy(SHM_NAME);
    if (ret < 0) {
        clog_log_error("shmem_ring_destroy failed, ret: %d\n", ret);
        return -ret;
    }

    ret = deinit_read_ring_buf_data_thread(g_clog_cfg);
    if (ret < 0) {
        clog_log_error("deinit read ring buf data thread fail \n");
        return -ret;
    }

    ret = deinit_module_log_files(g_clog_cfg);
    if (ret < 0) {
        clog_log_error("deinit module log files fail \n");
        return -ret;
    }

    free(g_clog_cfg);
    g_clog_cfg = NULL;

    return ret;
}

/************************ ring buf interface ************************/

/**
 * @brief Initialize clog system interface.
 *
 * This function initializes the clog system interface by loading configuration from a JSON file,
 * initializing log files, log ring buffer, module buffers, and mutexes.
 *
 * @param cfg_file Path to the JSON configuration file.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Configuration file path is null or empty
 *         - -RET_ERR: JSON file parse error
 *         - -RET_ERR: Log file initialization fails
 *         - -RET_ERR: Log ring buffer initialization fails
 *         - -RET_ERR: Module buffer and mutex initialization fails
 *         - -RET_ERR: Thread creation fails
 */
int32_t clog_system_init_interface(const char *cfg_file)
{
    return clog_system_init(cfg_file);
}

/**
 * @brief Deinitialize clog system interface.
 *
 * This function deinitializes the clog system interface by deinitializing log ring buffer,
 * module buffers, and mutexes, and freeing allocated memory.
 *
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Log ring buffer deinitialization fails
 *         - -RET_ERR: Module buffer and mutex deinitialization fails
 *         - -RET_ERR: Thread deinitialization fails
 */
int32_t clog_system_deinit_interface(void)
{
    if (g_clog_cfg == NULL) {
        clog_log_error("clog system not init\n");
        return -RET_ERR;
    }
    return clog_system_deinit();
}

/**
 * @brief Write one log message to the log ring buffer interface.
 *
 * This function writes one log message to the log ring buffer interface.
 *
 * @param data Pointer to the log message data.
 * @param size Size of the log message data.
 * @return int32_t RET_OK (0) on success; negative error code on failure:
 *         - -RET_ERR: Log ring buffer write fails
 */
int32_t write_one_log_message_interface(shared_mem_t* shared_mem, uint8_t *data, size_t size)
{
    if (shared_mem == NULL) {
        clog_log_error("shared_mem is null\n");
        return -RET_ERR;
    }
    return shmem_ring_enqueue(shared_mem, data, size);
}

int32_t write_one_log_message_to_ring_buf_interface(uint8_t *data, size_t size)
{
    return write_one_log_message_to_ring_buf(data, size);
}

/* This interface only supports dlsym calls */
int32_t clog_write_device_init_interface(clog_entry_t *entry)
{
    return clog_write_device_init(entry);
}

/* This interface only supports dlsym calls */
int32_t clog_write_entry_interface(clog_entry_t *entry)
{
    return clog_write_entry_to_list(entry);
}